package com.czl.common.redisbase;

import cn.hutool.core.collection.CollectionUtil;
import com.czl.common.util.Tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {

    private static final Logger logger = LoggerFactory.getLogger(RedisUtils.class);




    @Resource
    private RedisTemplate<String,  Object> redisTemplate;




    private int poolSize = 0;

    public static boolean hashKey = true; // keyhashtype枚举


    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     * @return
     */
    public  boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            logger.error( "expire exception", key, time,e );
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            logger.error( "hasKey exception", key,e );
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                for (String k : key) {
                    redisTemplate.delete(k);
                }
            }
        }
    }

    // ============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 批量获取key
     *
     * @param keys 键数组
     * @return 值
     */
    public List<Object> gets(String... keys) {
        return gets(Arrays.asList(keys));
    }

    /**
     * 批量获取key
     *
     * @param keys 键集合
     * @return 值
     */
    public List<Object> gets(Collection<String> keys) {

        List<Object> res = CollectionUtil.newArrayList();

        Map<Integer, List<String>> kMap =  CollectionUtil.newHashMap();
        for (String key : keys) {
            Integer index = Math.abs(key.hashCode()) % poolSize;
            List<String> klist = kMap.get(index);
            if (klist == null) {
                klist = CollectionUtil.newArrayList();
                kMap.put(index, klist);
            }
            klist.add(key);
        }
        Iterator<Integer> keyits = kMap.keySet().iterator();
        while (keyits.hasNext()) {
            Integer index = keyits.next();
            List<Object> objList = redisTemplate.opsForValue().multiGet(kMap.get(index));
            res.addAll(objList);
        }
        return res;
    }
    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            logger.error("set exception", key, value,e );
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        if (time > Integer.MAX_VALUE) {
            time = Integer.MAX_VALUE;// 临时处理方案，由于
        }
        try {
            set(key, value);
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            logger.error( "set exception", key, value, time,e );
            return false;
        }
    }

    /**
     * 如果缓存中不存在该key，则更新缓存中的值并设置过期时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true 成功 false 失败
     */
    public boolean setnx(String key, Object value, long time) {

        Boolean result = false;

        try {
            if (time > 0) {
                result = redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
            } else {
                result = redisTemplate.opsForValue().setIfAbsent(key, value);
            }
        } catch (Exception e) {
            logger.error( "setnx exception", key, value, time,e );
            result = false;
        }

        return (result != null) ? result.booleanValue() : false;
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(final String key, final long delta, final long time) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        String key1 = key;
        return redisTemplate.execute(new SessionCallback<Long>() {

            @Override
            @SuppressWarnings("unchecked")
            public Long execute(@SuppressWarnings("rawtypes") RedisOperations operations) throws DataAccessException {
                // operations.watch(key1);
                operations.multi();
                operations.opsForValue().increment(key1, delta);
                operations.expire(key1, time, TimeUnit.SECONDS);
                List<?> res = operations.exec();
                if (res != null && res.size() > 0) {
                    return Long.parseLong(res.get(0).toString());
                }
                return null;

            }
        });
    }

    /**
     * 递增 不支持事务临时方案
     * @param key
     * @param delta
     * @param time
     * @return
     */
    public long incr2(final String key, final long delta, final long time) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        Long increment = redisTemplate.opsForValue().increment(key, delta);
        redisTemplate.expire(key, time, TimeUnit.SECONDS);

        return increment;
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta, final long time) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        Long decrement = redisTemplate.opsForValue().decrement(key, delta);
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
        return decrement;
    }

    // ================================Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    public Map<String, Object> getHashMap(String key) {
        Map<Object, Object> m = hmget(key);
        Map<String, Object> res = new HashMap<String, Object>();
        if (m != null) {
            for (Map.Entry<Object, Object> entry : m.entrySet()) {
                res.put(Tools.toString(entry.getKey()), entry.getValue());
            }
        }
        return res;
    }

    /**
     * HashSet
     *
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            logger.error( "setnx exception", key, Tools.objectToJsonString(map),e );
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {

        try {
            String key1 = key;
            SessionCallback<Boolean> myscb = new SessionCallback<Boolean>() {
                @Override
                @SuppressWarnings("unchecked")
                public Boolean execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    operations.multi();
                    operations.opsForHash().putAll(key1, map);
                    if (time > 0) {
                        operations.expire(key1, time, TimeUnit.SECONDS);
                    }
                    List<?> execRes = operations.exec();
                    return Boolean.parseBoolean(execRes.get(0).toString());
                }

            };
            return redisTemplate.execute(myscb);
        } catch (Exception e) {
            logger.error( "hmset exception", key, Tools.objectToJsonString(map), time,e );
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            logger.error( "hset exception", key, item, Tools.objectToJsonString(value),e );
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        if (time > Integer.MAX_VALUE) {
            time = Integer.MAX_VALUE;// 临时处理方案，由于
        }
        final long _time = time;
        try {
            String key1 = key;
            SessionCallback<Boolean> myscb = new SessionCallback<Boolean>() {
                @Override
                @SuppressWarnings("unchecked")
                public Boolean execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    operations.multi();
                    operations.opsForHash().put(key1, item, value);
                    if (_time > 0) {
                        operations.expire(key1, _time, TimeUnit.SECONDS);
                    }
                    List<?> execRes = operations.exec();
                    return Boolean.parseBoolean(execRes.get(0).toString());
                }

            };
            return redisTemplate.execute(myscb);
        } catch (Exception e) {
            logger.error( "hset exception", key, item, Tools.objectToJsonString(value), time,e );
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key   键
     * @param item  项
     * @param delta 增量(大于0)
     * @return
     */
    public Long hincr(String key, String item, long delta) {
        if (delta <= 0) {
            throw new IllegalArgumentException("delta must be positive");
        }
        return redisTemplate.opsForHash().increment(key, item, delta);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key   键
     * @param item  项
     * @param delta 增量(大于0)
     * @param time  过期时间(秒)
     * @return
     */
    @SuppressWarnings("unchecked")
    public Long hincr(final String key, final String item, final long delta, final long time) {

        if (delta <= 0) {
            throw new IllegalArgumentException("delta must be positive");
        }

        if (time <= 0) {
            throw new IllegalArgumentException("time must be positive");
        }

        try {

            String key1 = key;
            return redisTemplate.execute(new SessionCallback<Long>() {

                @Override
                public Long execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    operations.multi();
                    operations.opsForHash().increment(key1, item, delta);
                    operations.expire(key1, time, TimeUnit.SECONDS);
                    return Long.parseLong(operations.exec().get(0).toString());
                }
            });

        } catch (Exception e) {
            logger.error( "hincr exception", key, item, delta, time,e );
            return null;
        }

    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }

    // ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            logger.error( "sGet exception", key,e );
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            logger.error( "sHasKey exception", key, Tools.objectToJsonString(value),e );
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            logger.error( "sSet exception", key, Tools.objectToJsonString(values),e );
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            long rtn = redisTemplate.opsForSet().add(key, values);
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return rtn;
        } catch (Exception e) {
            logger.error( "sSetAndTime exception", key, time, Tools.objectToJsonString(values),e );
            return 0;
        }
    }

    /**
     * 将set数据放入缓存并设置时间 临时方案
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime2(String key, long time, Object... values) {

        Long add = redisTemplate.opsForSet().add(key, values);
        if (time > 0) {
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
        return add;
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            logger.error( "sGetSetSize exception", key,e );
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            logger.error( "setRemove exception", key, Tools.objectToJsonString(values),e );
            return 0;
        }
    }

    /**
     * 随机返回几个值
     * @param key 键
     * @param count
     * @return
     */
    public List<Object> sRandomMembers(String key, int count) {

        List<Object> objects = redisTemplate.opsForSet().randomMembers(key, count);

        return objects;
    }
    // ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            logger.error( "lGet exception", key, start, end,e );
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            logger.error( "lGetListSize exception", key,e );
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            logger.error( "lGetIndex exception", key, index,e );
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            logger.error( "lSet exception", key, Tools.objectToJsonString(value),e );
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {

        try {
            String key1 = key;
            SessionCallback<Boolean> myscb = new SessionCallback<Boolean>() {
                @Override
                @SuppressWarnings("unchecked")
                public Boolean execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    // operations.watch(key);
                    operations.multi();
                    operations.opsForList().rightPush(key1, value);
                    operations.expire(key1, time, TimeUnit.SECONDS);
                    List<?> execRes = operations.exec();
                    return Boolean.parseBoolean(execRes.get(0).toString());
                }
            };
            boolean rb = redisTemplate.execute(myscb);
            return rb;
        } catch (Exception e) {
            logger.error( "lSet exception", key, Tools.objectToJsonString(value), time,e );
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            logger.error( "lSet exception", key, Tools.objectToJsonString(value),e );
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {

        try {
            String key1 = key;
            SessionCallback<Boolean> myscb = new SessionCallback<Boolean>() {

                @Override
                @SuppressWarnings("unchecked")
                public Boolean execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    operations.multi();
                    operations.opsForList().rightPushAll(key1, value);
                    operations.expire(key1, time, TimeUnit.SECONDS);
                    List<?> execRes = operations.exec();
                    return Boolean.parseBoolean(execRes.get(0).toString());
                }
            };
            boolean rb = redisTemplate.execute(myscb);
            return rb;
        } catch (Exception e) {
            logger.error( "lSet exception", key, Tools.objectToJsonString(value), time,e );
            return false;
        }

    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            logger.error( "lUpdateIndex exception", key, index, Tools.objectToJsonString(value),e );
            return false;
        }
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            logger.error( "lRemove exception", key, count, Tools.objectToJsonString(value),e );
            return 0;
        }
    }

    /**
     * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
     * @param key
     * @param start
     * @param end
     */
    public void lTrim(String key, long start, long end) {
        try {
            redisTemplate.opsForList().trim(key, start, end);
        } catch (Exception e) {
            logger.error( "lTrim exception", key, start, end,e );
        }
    }

    /**
     * 添加zset
     *
     * @param key
     * @param value
     * @param score
     * @param time  秒
     * @return
     */
    @SuppressWarnings("unchecked")
    public boolean zsSet(String key, Object value, Double score, long time) {
        try {
            String key1 = key;
            SessionCallback<Boolean> myscb = new SessionCallback<Boolean>() {
                @Override
                public Boolean execute(@SuppressWarnings("rawtypes") RedisOperations operations)
                        throws DataAccessException {
                    operations.multi();
                    operations.opsForZSet().add(key1, value, score);
                    operations.expire(key1, time, TimeUnit.SECONDS);
                    List<?> execRes = operations.exec();
                    return Boolean.parseBoolean(execRes.get(0).toString());
                }
            };
            boolean rb = redisTemplate.execute(myscb);
            return rb;
        } catch (Exception e) {
            logger.error( "zsSet exception", key, Tools.objectToJsonString(value), score, time,e );
            return false;
        }
    }

    @SuppressWarnings("unchecked")
    public boolean zsSet2(String key, Object value, Double score, long time) {
        try {
            Boolean rb = redisTemplate.opsForZSet().add(key, value, score);
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
            return rb;
        } catch (Exception e) {
            logger.error( "zsSet exception", key, Tools.objectToJsonString(value), score, time,e );
            return false;
        }
    }

    /**
     * @param key   键
     * @param value 项
     * @param delta 增量(大于0)
     * @return
     * @title zSet元素增加权重
     */
    public Double zSetIncrScore(String key, Object value, double delta, Long time) {
        if (delta <= 0) {
            throw new IllegalArgumentException("delta must be positive");
        }

        String key1 = key;
        return redisTemplate.execute(new SessionCallback<Double>() {
            @Override
            @SuppressWarnings("unchecked")
            public Double execute(@SuppressWarnings("rawtypes") RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForZSet().incrementScore(key1, value, delta);
                operations.expire(key1, time, TimeUnit.SECONDS);
                List<?> res = operations.exec();
                if (res != null && res.size() > 0) {
                    return Double.parseDouble(res.get(0).toString());
                }
                return null;

            }
        });
    }

    /**临时
     * @param key   键
     * @param value 项
     * @param delta 增量(大于0)
     * @return
     * @title zSet元素增加权重
     */
    public Double zSetIncrScore2(String key, Object value, double delta, Long time) {
        if (delta <= 0) {
            throw new IllegalArgumentException("delta must be positive");
        }

        String key1 = key;
        Double d=redisTemplate.opsForZSet().incrementScore(key, value, delta);
        redisTemplate.expire(key1, time, TimeUnit.SECONDS);
        return d;
    }


    /**
     * 获取zset中指定部分数据
     *
     * @param key
     * @param start
     * @param end
     * @param ofset
     * @param count
     * @return
     */
    public Set<Object> zrangeByScore(String key, double start, double end, long ofset, long count) {
        try {
            return redisTemplate.opsForZSet().rangeByScore(key, start, end, ofset, count);
        } catch (Exception e) {
            logger.error( "zrangeByScore exception", key, start, end, ofset, count,e );
            return null;
        }
    }

    /**
     * 获取zset中指定部分数据 从高到低排序的排序集
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<TypedTuple<Object>> zreverseRangeWithScores(String key, long start, long end) {
        try {
            return redisTemplate.opsForZSet().reverseRangeWithScores(key, start, end);
        } catch (Exception e) {
            logger.error( "zreverseRangeWithScores exception", key, start, end,e );
            return null;
        }
    }

    /**
     * 将所给数据根据所给关键字，找到对应有序set，从中删除
     *
     * @param key
     * @param values
     * @return
     */
    public Long removeFromZSet(String key, Object... values) {
        try {
            return redisTemplate.opsForZSet().remove(key, values);
        } catch (Exception e) {
            logger.error( "removeFromZSet exception", key, Tools.objectToJsonString(values),e );
            return null;
        }
    }

    /**
     * 返回有序zset中指定成员的分数
     *
     * @param key
     * @return
     */
    public Double zscore(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
     * Intersect sorted sets at {@code key} and {@code otherKey} and store result in destination {@code destKey}.
     * @param key
     * @param otherKey
     * @param destKey
     * @return
     */
    public Long zinterstore(String key, String otherKey, String destKey){
        return redisTemplate.opsForZSet().intersectAndStore(key, otherKey, destKey);
    }

    /**
     * 获取zset中元素数量
     *
     * @param key
     * @return
     */
    public Long getZSetSize(String key) {
        return redisTemplate.opsForZSet().size(key);
    }

    /**
     * 模糊查询获取key值
     *
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 使用Redis的消息队列
     *
     * @param channel
     * @param message 消息内容
     */
    public void convertAndSend(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }

    // =========BoundListOperations 用法 start============

    /**
     * 将数据添加到Redis的list中（从右边添加）
     *
     * @param listKey
     * @param values  待添加的数据
     */
    public void addToListRight(String listKey, long timeOut, TimeUnit unit, Object... values) {
        // 绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate
                .boundListOps(listKey);
        // 插入数据
        boundValueOperations.rightPushAll(values);
        // 设置过期时间
        boundValueOperations.expire(timeOut, unit);
    }

    /**
     * 根据起始结束序号遍历Redis中的list
     *
     * @param listKey
     * @param start   起始序号
     * @param end     结束序号
     * @return
     */
    public List<Object> rangeList(String listKey, long start, long end) {
        // 绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate
                .boundListOps(listKey);
        // 查询数据
        return boundValueOperations.range(start, end);
    }

    /**
     * 弹出右边的值 --- 并且移除这个值
     *
     * @param listKey
     */
    public Object rifhtPop(String listKey) {
        // 绑定操作
        BoundListOperations<String, Object> boundValueOperations = redisTemplate
                .boundListOps(listKey);
        return boundValueOperations.rightPop();
    }

    private static final String lockLua = "local num = redis.call('incr', KEYS[1])\n" + "if tonumber(num) == 1 then\n"
            + "redis.call('expire', KEYS[1], ARGV[1])\n" + "return true\n" + "else\n" + "return false\n" + "end\n";

    @Deprecated
    public boolean tryLock(String key, long lockMilliseconds, Long waitMilliseconds) {
        try {
            synchronized (key.intern()) {
                DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
                redisScript.setScriptText(lockLua);
                redisScript.setResultType(Boolean.class);
                List<String> keys = Arrays.asList(key);
                long stime = System.currentTimeMillis();
                while (System.currentTimeMillis() - stime <= (waitMilliseconds + 1)) {
                    boolean result = redisTemplate.execute(redisScript, keys, lockMilliseconds / 1000);
                    if (result) {
                        return result;
                    }
                    Tools.sleep(10l);
                }
            }
        } catch (Exception e) {
            logger.error( "tryLock exception", key, lockMilliseconds, waitMilliseconds,e );
        }
        return false;
    }

    @Deprecated
    public boolean unLock(String key) {
        if (key != null && key.trim().length() > 0) {
            return redisTemplate.delete(key);
        }
        return false;
    }



    /**
     * 按score区间 降序获取数据
     *
     * @param key
     * @param max    score最大值
     * @param min    score最小值
     * @param offset 角标起始值
     * @param count  数目
     * @return
     */
    public Set<Object> zReverseRangeByScore(String key, double min, double max, long offset, long count) {
        try {
            return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max, offset, count);
        } catch (Exception e) {
            logger.error( "zReverseRangeByScore exception", key, min, max, offset, count,e );
            return null;
        }
    }

    /**
     * 按score 降序获取数据
     *
     * @param key
     * @return
     */
    public Set<Object> zReverseRangeByScore(String key, double min, double max) {
        try {
            return redisTemplate.opsForZSet().reverseRangeByScore(key, min, max);
        } catch (Exception e) {
            logger.error( "zReverseRangeByScore exception", key, min, max,e );
            return null;
        }
    }

    /**
     * 当前obj对应的排名 按正序
     *
     * @param key
     * @param obj
     * @return
     */
    public Long zRank(String key, Object obj) {
        try {
            return redisTemplate.opsForZSet().rank(key, obj);
        } catch (Exception e) {
            logger.error( "zRank exception", key, Tools.objectToJsonString(obj),e );
            return null;
        }
    }

    /**
     * 获取zset元素下标（倒序）
     *
     * @param key
     * @param obj
     * @return
     */
    public Long zReverseRank(String key, Object obj) {
        try {
            return redisTemplate.opsForZSet().reverseRank(key, obj);
        } catch (Exception e) {
            logger.error( "zReverseRank exception", key, Tools.objectToJsonString(obj),e );
            return null;
        }
    }

    /**
     * 随机获取set中元素并删除
     *
     * @param key
     * @param count
     * @return
     */
    public List<Object> sPop(String key, int count) {
        try {
            return redisTemplate.opsForSet().pop(key, count);
        } catch (Exception e) {
            logger.error( "sPop exception", key, count,e );
            return null;
        }

    }

    public Long removeRangeByScore(String key,double min,double max){
        try {
            return redisTemplate.opsForZSet().removeRangeByScore(key, min, max);
        }catch (Exception e){
            logger.error( "removeRangeByScore exception",  key, min, max,e );
            return null;
        }
    }


    /**
     * 可选参数：
     *
     *     server：有关Redis服务器的常规信息
     *     clients：客户端连接部分
     *     memory：内存消耗相关信息
     *     persistence：RDB和AOF相关信息
     *     stats：一般统计
     *     replication：主/副本复制信息
     *     cpu：CPU消耗统计信息
     *     commandstats：Redis命令统计
     *     cluster：“ Redis群集”部分
     *     keyspace：与数据库相关的统计
     *
     * 它还可以采用以下值：
     *
     *     all：返回所有部分
     *     default：仅返回默认的部分集
     * @param key
     * @return
     */
    public Properties info(String key){
        Properties info = redisTemplate.getRequiredConnectionFactory().getConnection().info(key);

        return info;
    }

}